package re56.equipment;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

import re56.layer.ENBPhysicalLayer;
import re56.layer.RadioLayer;
import re56.packet.PDCCHPacket;
import re56.packet.PDSCHPacket;
import re56.packet.Packet;


// LTE Base Station.
public class EvolvedNodeB extends Equipment
{
	private HashMap<String, UserEquipment> ConnectedUE;
	
	public EvolvedNodeB(RadioLayer radioLayerDL, RadioLayer radioLayerUL)
	{
		super(radioLayerDL, radioLayerUL);
		
		Phy = new ENBPhysicalLayer(Mac, radioLayerDL, radioLayerUL);
		
		radioLayerDL.useRadioLayer(this);
		radioLayerUL.useRadioLayer(this);
		
		
		ConnectedUE = new HashMap<String, UserEquipment>();
		
		
		// http://netseminar.stanford.edu/seminars/01_29_09.pdf
		// Typical Power for a Macro Cell ~43 dBm
		Phy.setPower(43);
		
		Phy.setSensitivity(-110);
	}
	
	
	public UserEquipment getUE(String IMSI)
	{
		return ConnectedUE.get(IMSI);
	}
	
	public UserEquipment getUE(byte[] MACAddr)
	{
		Set<String> set = ConnectedUE.keySet();
		Iterator<String> it = set.iterator();
		
		while(it.hasNext())
		{
			UserEquipment ue = ConnectedUE.get(it.next());
			
			if(ue.getMacLayer().getMACAddress().equals(MACAddr))
				return ue;
		}
		
		return null;
	}
	
	
	public int getUECount()
	{
		return ConnectedUE.size();
	}
	
	
	public void connectUE(UserEquipment ue)
	{
		// actually add the UE to our list.
		ConnectedUE.put(ue.getIMSI(), ue);
		
		// and then recompute the RB allocation.
		compute();
	}
	
	
	public void disconnectUE(UserEquipment ue)
	{
		if(!ConnectedUE.containsKey(ue.getIMSI()))
			return;
		
		// remove him from our list.
		ConnectedUE.remove(ue.getIMSI());
		
		// and then recompute the RB allocation.
		compute();
	}
	
	
	public void compute()
	{
		// let's implement the Allocation Algorithm
		int availableRB_DL = Phy.getDLRadioLayer().getRBNumber();
		int availableRB_UL = Phy.getULRadioLayer().getRBNumber();
		
		System.out.println("Compute Allocation: [");
		System.out.println("\tDL: "+availableRB_DL+" UL: "+availableRB_UL+" Resource Blocks Available.");
		System.out.println("\t"+getUECount() +" Users connected.");
		
		float alloc_dl = availableRB_DL / (float) getUECount();
		float alloc_ul = availableRB_UL / (float) getUECount();
		
		int first_block_ul = 0;
		int first_block_dl = 0;
		
		Set<String> set = ConnectedUE.keySet();
		Iterator<String> it = set.iterator();
		
		while(it.hasNext())
		{
			UserEquipment ue = ConnectedUE.get(it.next());
			PDCCHPacket pack = new PDCCHPacket();
			
			for(int i = first_block_ul; i < (first_block_ul+Math.ceil(alloc_ul)); i++)
				pack.addRB_UL(i);
			
			for(int i = first_block_dl; i < (first_block_dl+Math.ceil(alloc_dl)); i++)
				pack.addRB_DL(i);
			
			
			Mac.sendPacket(pack, ue);
			
			first_block_ul += Math.ceil(alloc_ul);
			first_block_dl += Math.ceil(alloc_dl);
		}
		
		System.out.println("] Allocation Done.");
	}
	
	@Override
	public void receive(Packet p)
	{
		String mess = new String(p.getData());
		System.out.println("\t\t\t\t\teNB just received a Packet. Content: "+mess);
		
		// each time the eNB receives a packet, it responds. To generate trafic.
		PDSCHPacket pack = new PDSCHPacket();
		mess += " World !";
		pack.setData(mess.getBytes());
		
		// retrieve the UE in our list.
		UserEquipment ue = getUE(p.getHeader().getSource());
		
		if(ue == null)
			return;
		
		send(pack, ue);
	}

	@Override
	public void send(Packet p, Equipment e)
	{
		super.send(p, e);
	}
}
